//
// Created by feng on 19-3-28.
//

#ifndef SOLARENERGY_CHIER_RAYTRACINGPIPELINE_H
#define SOLARENERGY_CHIER_RAYTRACINGPIPELINE_H

#include <driver_types.h>
#include <memory>
#include <string>
#include <tuple>
#include <vector>

#include <vector_types.h>

/**
 * ResultData---直接从RQMCRT中摘过来的，一句代码都没改
 */
/**
 * TODO::RayTracingPipeline中的result_data_存在重复释放空间的问题，待解决------使用共享内存或许可以解决
 */

// struct ResultData {
//     float *data_p_before_smooth;        //smooth应该指的就是滤波的意思
//     float *data_p_after_smooth;
//     int2 resolution;                    // 输出的图片的分辨率
//     std::string output_file_path;       //输出文件路径
//     float receiver_pixel;
//     int ray_density;                    //光线个数？？
//     int toatl_reflect_count;
//     int receiver_type;

//     // 再加几项：
//     float total_power;
//     float maximum_flux;
//     int2 maximum_location;
//     float* device_data_after_smooth;    // 保存滤波后，在device端的结果

//     dim3 nBlocks;       // 保存运行时cuda的设置
//     int nThreads;

// ResultData(float *before_p = nullptr, float *after_p = nullptr, int2 r = {0, 0}, float p =
// 0.0f,int rt=0)
//             :   data_p_before_smooth(before_p), data_p_after_smooth(after_p),
//                 resolution(r), receiver_pixel(p),receiver_type(rt),
//                 total_power(0.0), maximum_flux(0.0), maximum_location(int2(0,0))
//                 {}

//     ~ResultData() {
//         std::cout <<"------- ~ResultData -----\n";
//         if (data_p_before_smooth != nullptr) {
//             std::cout <<"!!!!!  free data_p_before_smooth !!!!!\n";
//             delete (data_p_before_smooth);
//         }
//         if (data_p_after_smooth) {
//             std::cout <<"!!!!!  free data_p_after_smooth !!!!!\n";
//             delete (data_p_after_smooth);
//         }
//     }
// };

namespace solar
{
    class SceneConfiguration;
    class SceneField;
    class SceneLoader;
    namespace cuda
    {
        class CudaHeliostat;
        class CudaReceiver;
    } // namespace cuda
    class ResultData
    {
        public:
            // ResultData(){}
            ResultData(float* before_p = nullptr, float* after_p = nullptr, int2 r = {0, 0},
                       float p = 0.0f, int rt = 0)
                : data_p_before_smooth(before_p), data_p_after_smooth(after_p), resolution(r),
                  receiver_pixel(p), receiver_type(rt), total_power(0.0), maximum_flux(0.0),
                  maximum_location(int2(0, 0))
            {
            }
            ~ResultData();

            // private:
            float* data_p_before_smooth; // smooth应该指的就是滤波的意思
            float* data_p_after_smooth;
            int2 resolution;              // 输出的图片的分辨率
            std::string output_file_path; // 输出文件路径
            float receiver_pixel;
            int ray_density; // 光线个数？？
            int toatl_reflect_count;
            int receiver_type;

            float total_power;
            float maximum_flux;
            int2 maximum_location;
            float* device_data_after_smooth; // 保存滤波后，在device端的结果
            uchar4* device_fluxmap;          // 设备端fluxmap---存的是每个像素的颜色值
                                             // GLuint texture_id;              // fluxmap纹理id
    };

    /**
     * RayTRacing
     */
    class RayTracingPipeline
    {
        public:
            RayTracingPipeline();

            void setConfiguration(
                std::shared_ptr<SceneConfiguration> config);       // 供UI层调用，用来设置config_
            void setSceneField(std::shared_ptr<SceneField> field); // 供ui层调用，用来设置field_
            void setCloudFilePath(std::string cloud_file_path);

            auto getConfiguration()
                -> std::shared_ptr<SceneConfiguration>&;          // 供UI层调用，用来获取config_
            auto getSceneField() -> std::shared_ptr<SceneField>&; // 供ui层调用，用来获取field_
            // ResultData& getResultData();

            // 设置好config和field后，进行rayTracing
            void rayTracing(bool use_file = false, std::string_view json_file_path = "");

            // 仅执行光线跟踪，不执行其他操作
            auto rayTracingOnly(bool use_file = false,  std::string_view json_file_path = "",
                                cudaStream_t stream_ = nullptr) -> std::tuple<float*, int2>;
            
            // 仅执行光线跟踪，不执行其他操作
            // 仅用于UNIZAR参数拟合，单个矩形定日镜，定日镜中心与接收器中心高度相同
            auto rayTracingOnlyImagePlane(bool use_file = false,  std::string_view json_file_path = "",
                                cudaStream_t stream_ = nullptr) -> std::tuple<float*, int2>;

            // 画图并保存
            void plotFluxMap();

            // 从JSON文件中读取数据，把数据保存到SceneField、SceneConfiguration中
            void loadDataFromJSON(const std::string& json_file_path);

        private:
            // 获得子定日镜的点，
            auto setFlatRectangleHeliostatVertex(
                float3*& d_heliostat_vertexs,
                std::vector<std::shared_ptr<cuda::CudaHeliostat>>& heliostats, int start_id,
                int end_id) -> int;
            // 把当前rev的结果累加到result_data_上
            void addReceiverResult(std::shared_ptr<cuda::CudaReceiver> rev);

            // 把结果保存到.txt文件中 --- 不用python画图，这个函数可用可不用
            void saveResultToTxt(float& sum_power, float& maximum_flux, int2& maximum_location);

            //---------------------cuda及多线程 flux map-------------------------------------------

            // 找到结果中的最大值、最大值位置、以及总能量----利用cuda
            void analyzeResultDataViaCuda();

            // 计算openGL所需的纹理---即fluxmap ---使用cuda
            void generateFluxMapTextureViaCuda();

            // 将cuda计算出的flux map 纹理保存成png格式的图片
            void saveFluxMapToPng();

        public:
            static std::shared_ptr<ResultData> result_data_;

        private:
            // std::shared_ptr<solar::Scene> scene_;      // 场景---从场景中读取数据
            // std::shared_ptr<solar::RayTracingParameter> simulation_paras_; //仿真参数

            std::shared_ptr<SceneConfiguration> config_; // 保存场景中的全局设置
            std::shared_ptr<SceneField> field_;          // 保存镜场中所有的镜子、接收器
            // ResultData result_data_;            // 记录rayTRacing的结果

            // ！！！！！！！临时存档一下云层的地址！！！！！！！！！！！！！！！！！！！！！
            std::string cloud_file_path_;
            std::shared_ptr<SceneLoader> scene_loader_;
    };
} // namespace solar

#endif // SOLARENERGY_CHIER_RAYTRACINGPIPELINE_H
